package net.ds0101.FindTen;

import android.media.AudioManager;
import android.media.SoundPool;

//import android.media.SoundPool.OnLoadCompleteListener;

import net.ds0101.framework.android.neox.action.NXAction;
import net.ds0101.framework.android.neox.action.NXFadeIn;
import net.ds0101.framework.android.neox.action.NXFadeOut;
import net.ds0101.framework.android.neox.action.NXPlay;
import net.ds0101.framework.android.neox.action.NXRepeatForever;
import net.ds0101.framework.android.neox.util.NXEasingCurve;
import net.ds0101.framework.android.neox.util.NXTime;
import net.ds0101.framework.android.neox.animation.NXAbstractAnimation;
import net.ds0101.framework.android.neox.animation.NXAbstractAnimation.AnimationListener;
import net.ds0101.framework.android.neox.animation.NXAbstractAnimation.State;
import net.ds0101.framework.android.neox.animation.NXPropertyAnimation;
import net.ds0101.framework.android.neox.core.NXEventHandler;
import net.ds0101.framework.android.neox.core.NXMenuItemActionHandler;
import net.ds0101.framework.android.neox.core.NXNode;
import net.ds0101.framework.android.neox.core.NXNode.OnClickCallback;
import net.ds0101.framework.android.neox.main.NXDirector;
import net.ds0101.framework.android.neox.main.NXScene;
import net.ds0101.framework.android.neox.util.NXResourceConverter;
import net.ds0101.framework.android.neox.widget.NXBitmapSheet;
import net.ds0101.framework.android.neox.widget.NXLabelBitmap;
import net.ds0101.framework.android.neox.widget.NXMenu;
import net.ds0101.framework.android.neox.widget.NXMenuItem;
import net.ds0101.framework.android.neox.widget.NXMenuItemImage;
import net.ds0101.framework.android.neox.widget.NXSprite;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;

public class GameScene extends NXScene {
	private enum TouchResult {
		NO_TOUCHED,
		RIGHT_TOUCHED,
		WRONG_TOUCHED
	};

    private enum SoundAlias {
        WRONG_CLICK,
        RIGHT_CLICK,
        TIMEUP,
        NEWRECORD,
        MAX_SOUND
    };

	public static final String HIGH_SCORE_SUFFIX = "highScore";
	public static final int MAX_TARGET_VALUE_RECALC_TIMES = 4;
	private static final int TARGET_VALUE = 10;
	public static final int SEPARATE = 3;
    private static final int TIME_PUNISHMENT = 400;
    private static final int TOTAL_TIME = NXTime.MSEL_PER_SECOND * 60 - 1;
	private int mRow, mCol;
	private Rect mPlayArea;
	private Tray mTraies[];
	private Point mTraiesPos[];
	private final int TRAY_GAP = 10;
	private final int TRAY_RADIUS = 35;
	private TouchResult mTouchResult = TouchResult.NO_TOUCHED;
	private int mScore;
	private int mHighScore;
	private NXLabelBitmap mScoreLabel;
	private NXLabelBitmap mCountdown;
	private NXLabelBitmap mHighScoreLabel;
	private NXLabelBitmap mScoreResult;
	private long mTimeTotal = NXTime.MSEL_PER_SECOND * 60;
	private long mTimeBegin;
	private boolean mRelayout;
	private NXMenu mMenu;
	private NXSprite mGameOver;
    private NXSprite mNewRecord;
    private NXAction mFlicker;
    private NXAction mFadeOut, mFadeIn;
    private SoundPool mSoundPool;
    private final static int MAX_SAMPLES = 6;
    private int mSoundId[] = new int [SoundAlias.MAX_SOUND.ordinal()];
    private int mSoundsLoaded;
    private boolean mTimeUp;
    private boolean mMute;
    private NXBitmapSheet mCountdownTexture;
    private NXAction mCountdownAction;
    private boolean mIsStart;
	
	public GameScene(Context context) {
		super(context);
		/*mRow = row;
		mCol = col;*/
    	
		int w = NXDirector.getScreenWidth();
		int h = NXDirector.getScreenHeight();
		
		if (w > h) {
			int t = w;
			w = h;
			h = t;
		}
		
		mCountdownTexture = new NXBitmapSheet(NXResourceConverter.getBitmapFromResource(this, R.drawable.countdown), 4, 1);
		addChild(mCountdownTexture);
		mCountdownTexture.position(NXDirector.screenCenter());
		mCountdownAction = new NXPlay(3000, new Point(0, 0), new Point(3, 0));
		NXAbstractAnimation anim = mCountdownAction.getAnimation();
		anim.setAnimationListener(new AnimationListener() {

			@Override
			public void onAnimationStart(NXAbstractAnimation animation) {
				NXNode target = ((NXPropertyAnimation)animation).target();
				target.visible(true);
			}

			@Override
			public void onAnimationFinish(NXAbstractAnimation animation) {
				NXNode target = ((NXPropertyAnimation)animation).target();
				target.visible(false);
				newTurn();
			}

			@Override
			public void onAnimationRepeat(NXAbstractAnimation animation) {
				// TODO Auto-generated method stub
			}

			@Override
			public void onAnimationStateChanged(NXAbstractAnimation animation,
					State newState, State oldState) {
				// TODO Auto-generated method stub
			}
		});
		
		Bitmap retrySkin = NXResourceConverter.getBitmapFromResource(this, R.drawable.retry);
		NXMenuItem retryButton = 
			new NXMenuItemImage(retrySkin, retrySkin, retrySkin, null);
		retryButton.setOnClickListener(new OnClickCallback() {
			@Override
			public void onClick(NXNode node) {
				countdown();
			}
		});
		retryButton.position(-(retryButton.width() / 1.3f), 0);
		
		Bitmap backSkin = NXResourceConverter.getBitmapFromResource(this, R.drawable.back);
		NXMenuItem backButton = 
			new NXMenuItemImage(backSkin, backSkin, backSkin, null);
		backButton.setOnClickListener(new OnClickCallback() {
			
			@Override
			public void onClick(NXNode node) {
				mNewRecord.stopAction(mFlicker);
				mCountdownTexture.stopAction(mCountdownAction);
				director().popScene();
				((MainEntry)director()).addAdsViewBottom();
			}
		});
		backButton.position((backButton.width() / 1.3f), 0);
		
		mMenu = new NXMenu(retryButton, backButton);
		mMenu.position(NXDirector.getScreenWidth() / 2, 30);
		addChild(mMenu, 1000); // Make menu at top of all node.
		
		Bitmap bigNumFont = NXResourceConverter.getBitmapFromResource(this, R.drawable.numbig);
		Bitmap smallNumFont = NXResourceConverter.getBitmapFromResource(this, R.drawable.numsmall);
		NXLabelBitmap.registerFontBitmap("BigNum", "0123456789':", bigNumFont, 12, 1);
		NXLabelBitmap.registerFontBitmap("SmallNum", "0123456789':", smallNumFont, 12, 1);
		
		mPlayArea = new Rect(0, h - w, w, h);
		/*int baseX = (w - (mCol - 1) * TRAY_GAP - mCol * TRAY_RADIUS * 2) / 2 - TRAY_RADIUS;
		int baseY = (mPlayArea.top + h - (mRow - 1) * TRAY_GAP - mRow * TRAY_RADIUS * 2) / 2 - TRAY_RADIUS;
		if (mRow > 0 && mCol > 0) {
			int totalItem = mRow * mCol;
			mTraies = new Tray [totalItem];
			mTraiesPos = new Point[totalItem];
			for (int i = 0; i < totalItem; ++i) {
				mTraiesPos[i] = new Point(baseX + (i % mCol + 1) * TRAY_RADIUS + (i % mCol + 1) * TRAY_RADIUS + ((i % mCol) * TRAY_GAP), 
						baseY + (i / mRow + 1) * TRAY_RADIUS  + (i / mRow + 1) * TRAY_RADIUS + ((i / mRow) * TRAY_GAP));
				mTraies[i] = new Tray(this, mTraiesPos[i], TRAY_RADIUS);
				addChild(mTraies[i]);
			}
		}*/
		
		NXSprite timeLabel = NXSprite.spriteWithBitmap(NXResourceConverter.getBitmapFromResource(this, R.drawable.label_time));
		timeLabel.position(NXDirector.getScreenWidth() / 2, (int) (NXDirector.getScreenHeight() / 7));
		addChild(timeLabel);
		
		NXSprite timer = NXSprite.spriteWithBitmap(NXResourceConverter.getBitmapFromResource(this, R.drawable.frame_timer));
		timer.position(NXDirector.getScreenWidth() / 2, timeLabel.positionY() + (int)(timer.height() / 1.4));
		addChild(timer);
		mCountdown = new NXLabelBitmap(countdownFormat(mTimeTotal), "BigNum", timer.positionX(), timer.positionY());
		addChild(mCountdown);
		
		
		NXSprite highScoreTitle = NXSprite.spriteWithBitmap(NXResourceConverter.getBitmapFromResource(this, R.drawable.hiscore));
		highScoreTitle.position((int) (highScoreTitle.width() / 1.8f), (int) (NXDirector.getScreenHeight() / 3.5));
		addChild(highScoreTitle);
		NXSprite scoreTitle = NXSprite.spriteWithBitmap(NXResourceConverter.getBitmapFromResource(this, R.drawable.score));
		scoreTitle.position(NXDirector.getScreenWidth() / 2 + (int) (scoreTitle.width() / 1.3), (int) (NXDirector.getScreenHeight() / 3.5));
		addChild(scoreTitle);
		mScoreLabel = new NXLabelBitmap("0", "BigNum", (int) (scoreTitle.positionX() + scoreTitle.width()), scoreTitle.positionY() + 2);
		addChild(mScoreLabel);
		mHighScoreLabel= new NXLabelBitmap(String.valueOf(mHighScore), "BigNum", (int) (highScoreTitle.positionX() + highScoreTitle.width() - 8), highScoreTitle.positionY() + 2);
		addChild(mHighScoreLabel);
		
		mGameOver = NXSprite.spriteWithBitmap(NXResourceConverter.getBitmapFromResource(this, R.drawable.gameover));
		mGameOver.position(NXDirector.getScreenWidth() / 2, (int) (NXDirector.getScreenHeight() / 1.55));
		mGameOver.visible(false);
		addChild(mGameOver);
		mScoreResult = new NXLabelBitmap("0", "BigNum", mGameOver.positionX(), (int) (NXDirector.getScreenHeight() / 1.44));
		mScoreResult.visible(false);
		addChild(mScoreResult);
        mNewRecord = NXSprite.spriteWithBitmap(NXResourceConverter.getBitmapFromResource(this, R.drawable.newrecord));
        mNewRecord.position(NXDirector.getScreenWidth() / 2, (int) (NXDirector.getScreenHeight() / 1.33));
        mNewRecord.visible(false);
        addChild(mNewRecord);
        
		mFlicker = new NXRepeatForever(NXFadeOut.action(800, NXEasingCurve.Type.InQuad));
		mFadeOut = NXFadeOut.action(150, NXEasingCurve.Type.Linear);
		mFadeIn = NXFadeIn.action(150, NXEasingCurve.Type.Linear);
		mFadeOut.getAnimation().setAnimationListener(new AnimationListener() {

			@Override
			public void onAnimationStart(NXAbstractAnimation animation) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onAnimationFinish(NXAbstractAnimation animation) {
				//Log.w("[NEOX]", "Traies fade out animation finish.");
				changeTrayValue();
				for (int i = 0; i < mTraies.length; ++i) {
					mTraies[i].attachAction(mFadeIn);
				}
			}

			@Override
			public void onAnimationStateChanged(NXAbstractAnimation animation,
					State newState, State oldState) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void onAnimationRepeat(NXAbstractAnimation animation) {
				// TODO Auto-generated method stub
				
			}
			
		});

        mSoundsLoaded = 0;
        mSoundPool = new SoundPool(MAX_SAMPLES, AudioManager.STREAM_MUSIC, 0);
        /*mSoundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
            public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
                if (0 == status) {
                    ++mSoundsLoaded;
                }
            }
        });*/
		mSoundId[SoundAlias.NEWRECORD.ordinal()] = mSoundPool.load(director(), R.raw.newrecord, 1);
		mSoundId[SoundAlias.RIGHT_CLICK.ordinal()] = mSoundPool.load(director(), R.raw.click, 1);
		mSoundId[SoundAlias.WRONG_CLICK.ordinal()] = mSoundPool.load(director(), R.raw.wrong, 1);
		mSoundId[SoundAlias.TIMEUP.ordinal()] = mSoundPool.load(director(), R.raw.timeup, 1);

	}
	
    private boolean allSoundsLoaded() {
		return !mMute;
        //return (SoundAlias.MAX_SOUND.ordinal() == mSoundsLoaded);
    }

	public static String countdownFormat(long msec) {
        String secsPart = String.valueOf((int)(msec / NXTime.MSEL_PER_SECOND));
        if (secsPart.length() < 2) {
            secsPart = "0" + secsPart;
        }
		String msecPart = String.valueOf((int)((msec % NXTime.MSEL_PER_SECOND) / 10));
        if (msecPart.length() < 2) {
            msecPart = "0" + msecPart;
        }
		String output = secsPart + "'" + msecPart;
		return output;
	}
	
	public void onEnter() {
		super.onEnter();
		
		mRow = ((MainEntry)director()).gameGridRow();
		mCol = ((MainEntry)director()).gameGridCol();

		SharedPreferences data = ((NXDirector)this.getContext()).getPreferences(Activity.MODE_PRIVATE);
		mHighScore = data.getInt(HIGH_SCORE_SUFFIX + String.valueOf(mRow), 0);
		mHighScoreLabel.text(String.valueOf(mHighScore));
		
		if (data.getInt(MainMenu.IS_MUTE, 0) == 0) {
			mMute = false;
		} else {
			mMute = true;
		}
		
		if (null != mTraies) {
			for (int n = 0; n < mTraies.length; ++n) {
				removeChild(mTraies[n]);
				mTraies[n] = null;
			}
			mTraies = null;
		}
		int baseX = (mPlayArea.right - (mCol - 1/* + 2*/) * TRAY_GAP - mCol * TRAY_RADIUS * 2) / 2 - TRAY_RADIUS;
		int baseY = (mPlayArea.top + mPlayArea.bottom - (mRow - 1/* + 2*/) * TRAY_GAP - mRow * TRAY_RADIUS * 2) / 2 - (int)(TRAY_RADIUS * 1.1);
		if (mRow > 0 && mCol > 0) {
			int totalItem = mRow * mCol;
			mTraies = new Tray [totalItem];
			mTraiesPos = new Point[totalItem];
			for (int i = 0; i < totalItem; ++i) {
				mTraiesPos[i] = new Point(baseX + (i % mCol + 1) * TRAY_RADIUS + (i % mCol + 1) * TRAY_RADIUS + ((i % mCol) * TRAY_GAP), 
						baseY + (i / mRow + 1) * TRAY_RADIUS  + (i / mRow + 1) * TRAY_RADIUS + ((i / mRow) * TRAY_GAP));
				mTraies[i] = new Tray(this, mTraiesPos[i], TRAY_RADIUS);
				addChild(mTraies[i]);
			}
		}
		
		countdown();
    }
    
	public void countdown() {
		playLayout();
		countdownLayout();
		mIsStart = false;
		mCountdownTexture.runAction(mCountdownAction);
	}
	
	public void newTurn() {
		mIsStart = true;
		playLayout();
		changeTrayValue();
		mTimeTotal = TOTAL_TIME;
		mTimeBegin = System.currentTimeMillis();
		mRelayout = false;
		mScore = 0;
	}
	
	public void changeTrayValue() {
		boolean hasTargetValue = false;
		int [] tmp = null;
		int retryTimes = 0;
		
		while ((!hasTargetValue) && retryTimes < MAX_TARGET_VALUE_RECALC_TIMES) {
			for (int i = 0; i < mRow * mCol/* - 1*/; ++i) {
				if (!hasTargetValue 
						&& 0 == (int)(Math.random() * mRow * mCol)) { 
					tmp = getTargetValueBy3Addend();
					hasTargetValue = true;
				} else {
					tmp = getRandomAddend();
				}
				mTraies[i].setValues(tmp);
				//Log.w("[NEOX]", (isTargetValue(tmp) ? "+ (" : "  (") + String.valueOf(tmp[0]) + ", " + String.valueOf(tmp[1]) + ", " + String.valueOf(tmp[2]) + ")");
			}
			++retryTimes;
		}
		
		if (!hasTargetValue) {
			mTraies[mRow * mCol - 1].setValues(getTargetValueBy3Addend());
		} /*else {
			mTraies[mRow * mCol - 1].setValues(getRandomAddend());
		}*/
	}
	
	public void traiesShaking() {
		for (Tray tray : mTraies) {
			tray.shaking();
		}
	}
	
	public void traiesStopShaking() {
		for (Tray tray : mTraies) {
			tray.stopShaking();
		}
	}
	
	public void hideTraies() {
		for (Tray tray : mTraies) {
			tray.visible(false);
		}
	}
	
	public void showTraies() {
		for (Tray tray : mTraies) {
			tray.visible(true);
		}
	}
	
	/*public void slippage() {
		NXAction slipOut = null;
		for (Tray tray : mTraies) {
			slipOut = new NXMove(50, tray.position(), new Point(tray.positionX() - NXDirector.getScreenWidth(), tray.positionY()), NXEasingCurve.Type.InQuad);
			tray.runAction(slipOut);
		}
		changeTrayValue();
		for (int i = 0; i < mTraies.length; ++i) {
			mTraies[i].position(mTraiesPos[i].x + NXDirector.getScreenWidth(), mTraies[i].positionY());
			NXAction slipIn = new NXMove(50, mTraies[i].position(), mTraiesPos[i], NXEasingCurve.Type.InQuad);
			mTraies[i].runAction(slipIn);
		}
	}*/

	public void slippage() {
		for (int i = 0; i < mTraies.length; ++i) {
			mTraies[i].attachAction(mFadeOut);
		}
	}

	@Override
	public void onScreenSizeChanged(int arg0, int arg1) {
		// TODO Auto-generated method stub
	}

	public void onResume() {
		mTimeBegin = System.currentTimeMillis();
	}
	
	@Override
	public void update(int arg0) {
		if (!mIsStart) {
			mCountdown.text(countdownFormat(TOTAL_TIME));
			mScoreLabel.text(String.valueOf(0));
			return ;
		}
		if (mTimeTotal > 0) {
			mTimeTotal -= (System.currentTimeMillis() - mTimeBegin);
			mTimeBegin = System.currentTimeMillis();
			if (mTimeTotal < 0) mTimeTotal = 0;
			mCountdown.text(countdownFormat(mTimeTotal));

            if (mTimeTotal < 6 * NXTime.MSEL_PER_SECOND) {
                if (!mTimeUp && allSoundsLoaded()) {
                    mSoundPool.play(mSoundId[SoundAlias.TIMEUP.ordinal()], 0.5f, 0.5f, 1, 0, 1.0f); 
                }
                mTimeUp = true;
            }
            //mSoundPool.stop(mSoundId[SoundAlias.RIGHT_CLICK.ordinal()]);
            mSoundPool.stop(mSoundId[SoundAlias.WRONG_CLICK.ordinal()]);
			synchronized (mTouchResult) {
				if (TouchResult.RIGHT_TOUCHED == mTouchResult) {
					mTouchResult = TouchResult.NO_TOUCHED;
					++mScore;
                    if (allSoundsLoaded()) {
                        mSoundPool.play(mSoundId[SoundAlias.RIGHT_CLICK.ordinal()], 1.0f, 1.0f, 0, 0, 1.0f); 
                    }
					slippage();
				} else if (TouchResult.WRONG_TOUCHED == mTouchResult) {
					mTouchResult = TouchResult.NO_TOUCHED;
                    if (allSoundsLoaded()) {
                        mSoundPool.play(mSoundId[SoundAlias.WRONG_CLICK.ordinal()], 1.0f, 1.0f, 2, 0, 1.0f); 
                    }
					traiesShaking();
                    mTimeTotal -= TIME_PUNISHMENT; // Punishment
                    if (mTimeTotal < 0) mTimeTotal = 0;
				}
				mTouchResult = TouchResult.NO_TOUCHED;
			}
			mScoreLabel.text(String.valueOf(mScore));
		} else {
			if (!mRelayout) {
				finishLayout();
                if (mScore > mHighScore) {
                    SharedPreferences data = ((NXDirector)getContext()).getPreferences(Activity.MODE_PRIVATE);
                    data.edit().putInt(HIGH_SCORE_SUFFIX + String.valueOf(mRow), mScore).commit();
                    mHighScore = mScore;
                    mHighScoreLabel.text(String.valueOf(mHighScore));
                    mNewRecord.visible(true);
                    mNewRecord.runAction(mFlicker);
                    if (allSoundsLoaded()) {
                        mSoundPool.play(mSoundId[SoundAlias.NEWRECORD.ordinal()], 0.5f, 0.5f, 0, 0, 1.0f); 
                    }
                }

				mRelayout = true;
			}
		}
	}
	
	public void finishLayout() {
		mGameOver.visible(true);
        mScoreResult.text(String.valueOf(mScore));
		mScoreResult.visible(true);
		traiesStopShaking();
		hideTraies();
		mMenu.position(mMenu.positionX(), (int) (NXDirector.getScreenHeight() / 1.1));
        mTimeUp = false;
	}
	
	public void countdownLayout() {
		traiesStopShaking();
		hideTraies();
	}
	
	public void playLayout() {
		mNewRecord.stopAction(mFlicker);
		mGameOver.visible(false);
        if (mNewRecord.visible()) {
            mNewRecord.visible(false);
            mNewRecord.stopAction(mFlicker);
        }
		mScoreResult.visible(false);
		mMenu.position(NXDirector.getScreenWidth() >> 1, 30);
		showTraies();
	}
	
	/*public void countDown() {
		Bitmap countDownTexture = NXResourceConverter.getBitmapFromResource(getContext(), R.drawable.countdown);
		final NXSprite countDown = new NXSprite(new NXBitmapSheet(countDownTexture, 3, 1));
		addChild(countDown);
		countDown.position(NXDirector.screenCenter());
		long timeCount = System.currentTimeMillis();
		int i = 2;
		while (i >= 0) {
			if (System.currentTimeMillis() - timeCount >= 1000) {
				timeCount = System.currentTimeMillis();
				countDown.setDisplayFrame(i--, 1);
			}
		}
		//NXAction action = new NXPlay(3000, new Point(2, 0), new Point(0, 0));
		//countDown.runAction(action);
	}*/
	
	public void touchResult(boolean result) {
		synchronized (mTouchResult) {
			mTouchResult = result ? TouchResult.RIGHT_TOUCHED : TouchResult.WRONG_TOUCHED;
		}
	}
	
	public static int[] getRandomAddend() {
		if (TARGET_VALUE > 1) {
			int[] addend = new int[SEPARATE];
			do {
				for (int i = 0; i < SEPARATE; ++i) {
					addend[i] = (int) (Math.random() * (TARGET_VALUE - 1));
				}
			} while (TARGET_VALUE == addend[0] + addend[1] + addend[2]);
			return addend;
		}
		return null;
	}
	
	public static int[] getTargetValueBy3Addend() {
		if (TARGET_VALUE > 1) {
			int[] addend = new int[3];
			addend[0] = (int) (Math.random() * (TARGET_VALUE - 1)) + 1;
			addend[1] = (int) (Math.random() * (TARGET_VALUE - addend[0]));
			addend[2] = TARGET_VALUE - addend[0] - addend[1];
			return (isTargetValue(addend)) ? addend : null; // prevent result invalid.
		}
		return null;
	}
	
	public static boolean isTargetValue(int [] addend) {
		int sum = 0;
		for (int i = 0; i < addend.length; ++i) {
			sum += addend[i];
		}
		return TARGET_VALUE == sum;
	}
}
